package myJs.myPkg.plotly

import java.lang.{Boolean => JBoolean, Double => JDouble, Integer => JInt}
import argonaut.Argonaut._
import argonaut.{Json, PrettyParams}
import shared.plotly.Codecs._
import shared.plotly.element.Color
import shared.plotly.internals.BetterPrinter
import shared.plotly.layout._

import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.{JSON, Promise}
import argonaut.Argonaut._
import argonaut.{Json, PrettyParams}
import myJs.myPkg.jsext.{JSOptionBuilder, OptMap, noOpts}
import shared.plotly.Trace

import scala.concurrent.Future
import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.JSON

object Plotly {

  private val printer = BetterPrinter(PrettyParams.nospace.copy(dropNullKeys = true))

  private def stripNulls(json: Json): js.Any = {
    // Remove empty objects
    JSON.parse(printer.render(json))
  }

  def newPlot(div: String, data: Seq[Trace], layout: Layout, config: PlotlyConfigOptions): js.Any = {
    g.Plotly.newPlot(
      div,
      stripNulls(data.asJson),
      stripNulls(layout.asJson),
      config
    )
  }

  def toImage(data: Seq[Trace], layout: Layout) = {
    g.Plotly.toImage(
      js.Dictionary(
        "data" -> stripNulls(data.asJson),
        "layout" -> stripNulls(layout.asJson),
      ),
      js.Dictionary(
        "height" -> 600,
        "width" -> 800
      )
    ).asInstanceOf[Promise[String]].toFuture
  }

  def newPlot(div: String, data: Seq[Trace]): Unit = {
    g.Plotly.newPlot(
      div,
      stripNulls(data.asJson)
    )
  }

  def plot(div: String, data: Trace, layout: Layout): Unit =
    g.Plotly.plot(
      div,
      stripNulls(data.asJson),
      stripNulls(layout.asJson)
    )

  def plot(div: String, data: Trace): Unit =
    g.Plotly.plot(
      div,
      stripNulls(data.asJson)
    )

  implicit class TraceOps(val trace: Trace) extends AnyVal {
    def plot(div: String, layout: Layout): Unit =
      Plotly.plot(div, trace, layout)

    @deprecated("Create a Layout and call plot(div, layout) instead", "0.8.0")
    def plot(
              div: String,
              title: String = null,
              legend: Legend = null,
              width: JInt = null,
              height: JInt = null,
              showlegend: JBoolean = null,
              xaxis: Axis = null,
              yaxis: Axis = null,
              xaxis1: Axis = null,
              xaxis2: Axis = null,
              xaxis3: Axis = null,
              xaxis4: Axis = null,
              yaxis1: Axis = null,
              yaxis2: Axis = null,
              yaxis3: Axis = null,
              yaxis4: Axis = null,
              barmode: BarMode = null,
              autosize: JBoolean = null,
              margin: Margin = null,
              annotations: Seq[Annotation] = null,
              plot_bgcolor: Color = null,
              paper_bgcolor: Color = null,
              font: Font = null,
              bargap: JDouble = null,
              bargroupgap: JDouble = null,
              hovermode: HoverMode = null,
              boxmode: BoxMode = null
            ): Unit =
      plot(
        div,
        Layout(
          title,
          legend,
          width,
          height,
          showlegend,
          xaxis,
          yaxis,
          xaxis1,
          xaxis2,
          xaxis3,
          xaxis4,
          yaxis1,
          yaxis2,
          yaxis3,
          yaxis4,
          barmode,
          autosize,
          margin,
          annotations,
          plot_bgcolor,
          paper_bgcolor,
          font,
          bargap,
          bargroupgap,
          hovermode,
          boxmode
        )
      )
  }

  implicit class TraceSeqOps(val traces: Seq[Trace]) extends AnyVal {
    def plot(div: String, layout: Layout, config: PlotlyConfigOptions): Unit =
      Plotly.newPlot(div, traces, layout, config)

  }

}

final case class PlotlyConfig(
                               displayModeBar: Boolean,
                             )

object PlotlyConfig {
  def apply(
             displayModeBar: Boolean = true,
           ) =
    new PlotlyConfig(
      displayModeBar,
    )
}

object PlotlyConfigOptions extends PlotlyConfigOptionsBuilder(noOpts)

class PlotlyConfigOptionsBuilder(val dict: OptMap) extends JSOptionBuilder[PlotlyConfigOptions, PlotlyConfigOptionsBuilder](new PlotlyConfigOptionsBuilder(_)) {

  def displayModeBar(v: Boolean) = jsOpt("displayModeBar", v)

}

trait PlotlyConfigOptions extends js.Object {

}
